/*
 * JMathApp.java
 */

package jmath;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jdesktop.application.Action;
import org.jdesktop.application.Application;
import org.jdesktop.application.SingleFrameApplication;
import org.jdesktop.application.Task;
import org.springframework.context.support.*;
import jmath.eval.*;
import jmath.io.*;
import org.jfree.chart.ChartPanel;
import java.io.*;
import org.apache.log4j.*;
import javax.swing.*;

/**
 * The main class of the application.
 */
public class JMathApp extends SingleFrameApplication {

	private Log log = LogFactory.getLog(JMathApp.class);

	private FileSystemXmlApplicationContext initContext;

	private IOUtils ioUtils;

	private JMathConsole jMathConsole;

	private JMathScripter jMathScripter;

	private JMathGraphPloter jMathGraphPloter;

	private ScriptEvaluator scriptEvaluator;

	private File currentlyOpenedFile;

	private static final String LOG4J_CONFIGURE_FILE = "log4j.properties";

	/**
	 * At startup create and show the main frame of the application.
	 */
	@Override
	protected void startup() {
		PropertyConfigurator.configure(LOG4J_CONFIGURE_FILE);

		initContext = new FileSystemXmlApplicationContext("applicationContext.xml");
		ioUtils = (IOUtils) initContext.getBean("ioUtils");
		jMathConsole = (JMathConsole) initContext.getBean("jMathConsole");
		jMathScripter = (JMathScripter) initContext.getBean("jMathScripter");
		jMathGraphPloter = (JMathGraphPloter) initContext.getBean("jMathGraphPloter");
		scriptEvaluator = (ScriptEvaluator) initContext.getBean("scriptEvaluator");
		JMathView jMathView = new JMathView(this);
		jMathConsole.setConsolePane(jMathView.getConsolePane());
		jMathScripter.setScriptPane(jMathView.getScriptPane());
		jMathView.getScriptPane().setVisible(false);
		((ChartPanel) jMathView.getGraphPanel()).setChart(jMathGraphPloter.createChart());
		show(jMathView);
	}

	/**
	 * This method is to initialize the specified window by injecting resources.
	 * Windows shown in our application come fully initialized from the GUI
	 * builder, so this additional configuration is not needed.
	 */
	@Override
	protected void configureWindow(java.awt.Window root) {
	}

	/**
	 * A convenient static getter for the application instance.
	 * 
	 * @return the instance of JMathApp
	 */
	public static JMathApp getApplication() {
		return Application.getInstance(JMathApp.class);
	}

	/**
	 * Main method launching the application.
	 */
	public static void main(String[] args) {
		launch(JMathApp.class, args);
	}

	@Action
	public void stopScript() {
		System.out.println("stopScript");
	}

	@Action
	public void openFile() {
		if (currentlyOpenedFile != null && !read(currentlyOpenedFile).equals(jMathScripter.readAllScript())) {
			int response = JOptionPane.showConfirmDialog(null, ioUtils.getMessage("MOD_FILE_SAVE_QUESTIONS"));
			if (JOptionPane.OK_OPTION == response) {
				saveFile();
				return;
			} else if (JOptionPane.CANCEL_OPTION == response) {
				return;
			}
		} else if (currentlyOpenedFile == null && jMathScripter.readAllScript() != null
				&& jMathScripter.readAllScript().length() > 0) {
			int response = JOptionPane.showConfirmDialog(null, ioUtils.getMessage("FILE_SAVE_QUESTIONS"));
			if (JOptionPane.OK_OPTION == response) {
				saveFileAs();
				return;
			} else if (JOptionPane.CANCEL_OPTION == response) {
				return;
			}
		}
		File file = new FileChooser().openFile();
		if (file != null) {
			jMathScripter.writeScript(read(file));
			currentlyOpenedFile = file;

			showFileName();
			showScriptPane();
		}
	}

	@Action
	public void createNew() {
		boolean wasSaved = false;
		if (currentlyOpenedFile != null && !read(currentlyOpenedFile).equals(jMathScripter.readAllScript())) {
			int response = JOptionPane.showConfirmDialog(null, ioUtils.getMessage("MOD_FILE_SAVE_QUESTIONS"));
			if (JOptionPane.OK_OPTION == response) {
				saveFile();
				return;
			} else if (JOptionPane.CANCEL_OPTION == response) {
				return;
			}
		} else if (currentlyOpenedFile == null && jMathScripter.readAllScript() != null
				&& jMathScripter.readAllScript().length() > 0) {
			int response = JOptionPane.showConfirmDialog(null, ioUtils.getMessage("FILE_SAVE_QUESTIONS"));
			if (JOptionPane.OK_OPTION == response) {
				saveFileAs();
				return;
			} else if (JOptionPane.CANCEL_OPTION == response) {
				return;
			}

		}
		jMathScripter.writeScript("");
		currentlyOpenedFile = null;
		showFileName();
		showScriptPane();
	}

	// ////////////////////////////////////////////////
	@Action
	public void saveFileAs() {
		File file = new FileChooser().getFileForSave();
		if (file != null) {
			save(file);
			currentlyOpenedFile = file;
			showFileName();
		}
	}

	@Action
	public void saveFile() {
		if (currentlyOpenedFile != null) {
			save(currentlyOpenedFile);
		} else {
			saveFileAs();
		}
	}

	private void save(File file) {
		if (file != null) {
			BufferedWriter writer = null;
			try {
				if (!file.exists()) {
					if (!file.createNewFile()) {
						jMathConsole.warn(ioUtils.getMessage("CANNOT_CREATE_FILE"));
					}
				}
				if (file.exists() && file.canWrite()) {
					writer = new BufferedWriter(new FileWriter(file));
					writer.write(jMathScripter.readAllScript());
				} else {
					jMathConsole.warn(ioUtils.getMessage("NO_RIGHTS_FOR_SAVE"));
				}
			} catch (Exception ex) {
				jMathConsole.error(ioUtils.getMessage("ERROR_WHILE_SAVE"));
				log.error(ex.getMessage());
			} finally {
				if (writer != null) {
					try {
						writer.close();
					} catch (Exception ignored) {
					}
				}
			}
		}
	}

	private String read(File file) {
		if (file != null) {
			BufferedReader reader = null;
			try {
				if (file.canRead()) {
					reader = new BufferedReader(new FileReader(file));
					StringBuilder builder = new StringBuilder();
					String line = reader.readLine();
					while (line != null) {
						builder.append(line).append("\n");
						line = reader.readLine();
					}
					return builder.toString();
				} else {
					jMathConsole.warn(ioUtils.getMessage("CANNOT_READ_FILE"));
				}
			} catch (Exception ex) {
				jMathConsole.error(ioUtils.getMessage("ERROR_WHILE_SAVE"));
				log.error(ex.getMessage());
			} finally {
				if (reader != null) {
					try {
						reader.close();
					} catch (Exception ignored) {
					}
				}
			}
		}
		return null;
	}

	private void showFileName() {
		String applicationName = getContext().getResourceMap(JMathApp.class).getString("Application.name");
		this.getMainFrame().setTitle(
				(currentlyOpenedFile != null ? currentlyOpenedFile.getName() : "undefined") + " - " + applicationName);
	}

	private void showScriptPane() {
		((JMathView) this.getMainView()).getScriptPane().setVisible(true);
		((JMathView) this.getMainView()).reiniteMenuForOpenedFile();
	}

	// ////////////////////////////////////////////////

	@Action
	public void clearnConsole() {
		jMathConsole.clear();
		jMathGraphPloter.clear();
		scriptEvaluator.init();
	}

	@Action
	public Task runScript() {
		return new RunScriptTask(org.jdesktop.application.Application.getInstance(jmath.JMathApp.class));
	}

	private class RunScriptTask extends org.jdesktop.application.Task<Object, Void> {
		RunScriptTask(org.jdesktop.application.Application app) {
			// Runs on the EDT. Copy GUI state that
			// doInBackground() depends on from parameters
			// to RunScriptTask fields, here.
			super(app);
		}

		@Override
		protected Object doInBackground() {
			scriptEvaluator.eval();
			// Your Task's code here. This method runs
			// on a background thread, so don't reference
			// the Swing GUI from here.
			return null; // return your result
		}

		@Override
		protected void succeeded(Object result) {
			// Runs on the EDT. Update the GUI based on
			// the result computed by doInBackground().
		}
	}

	public FileSystemXmlApplicationContext getInitContext() {
		return initContext;
	}

}
